package cn.dev.mybatis.handler.parse;

import cn.dev.mybatis.annotation.Param;
import cn.dev.mybatis.bean.SqlObject;
import cn.dev.mybatis.bean.SqlType;
import cn.dev.mybatis.handler.SqlParser;
import cn.dev.mybatis.util.ClassUtils;
import cn.dev.mybatis.util.ObjectUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractSqlParser implements SqlParser {

    SqlObject createSqlObject(SqlType sqlType, Method method, Object[] args) {
        SqlObject sqlObject;
        if (method.getGenericReturnType() instanceof ParameterizedType returnType) {
            sqlObject = new SqlObject(sqlType, method.getParameters(), returnType.getRawType(), returnType.getActualTypeArguments());
        } else {
            sqlObject = new SqlObject(sqlType, method.getParameters(), method.getReturnType());
        }
        sqlObject.setSql(getSql(method));

        // 解析sql
        parseSql(sqlObject, method, args);
        return sqlObject;
    }

    private void parseSql(SqlObject sqlObject, Method method, Object[] args) {
        TokenParser tokenParser = new TokenParser();
        tokenParser.parseSql(sqlObject.getSql(), sqlObject);
        if (args == null || args.length == 0) {
            return;
        }

        Map<String, Object> argsMap = getArgs(method, args);
        if (argsMap.isEmpty() && ClassUtils.isAllPrimaryObject(args)) {
            // 位置赋值
            sqlObject.setParamValue(args);
            return;
        }

        // 解析赋值
        String[] paramNames = sqlObject.getParamNames();
        Object[] values = new Object[paramNames.length];
        for (int i = 0; i < paramNames.length; i++) {
            values[i] = argsMap.get(paramNames[i]);
        }
        sqlObject.setParamValue(values);
    }

    private Map<String, Object> getArgs(Method method, Object[] args) {
        Parameter[] parameters = method.getParameters();
        HashMap<String, Object> paramMap = new HashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            Param param = parameters[i].getAnnotation(Param.class);
            if (param != null) {
                paramMap.put(param.value(), args[i]);
            }
        }

        // 解析参数中的对象
        for (Object arg : args) {
            if (ClassUtils.isNotPrimaryObject(arg)) {
                paramMap.putAll(ObjectUtils.toMap(arg));
            }
        }
        return paramMap;
    }


    abstract String getSql(Method method);
}
